#include "bcurl.h"

using namespace bhttp;

static size_t write_callback(void* data, size_t size, size_t nmember, void* stream)
{
	std::vector<char>* recv_buf = static_cast<std::vector<char>*>(stream);
	size_t write_size = size * nmember;
	recv_buf->insert(recv_buf->end(), (char*)data, (char*)data + write_size);
	return write_size;
}

static int closesocket_func(void *clientp, curl_socket_t item)
{
    fprintf(stderr, "closesocket_func: %d\n", (int)item);
    return 0;
}

bcurl::bcurl()
	: curl_(curl_easy_init())
{
}

bcurl::~bcurl()
{
	if (curl_)
		curl_easy_cleanup(curl_);
	if (mime_)
		curl_mime_free(mime_);
	if (headers_)
		curl_slist_free_all(headers_);
}

bool bcurl::init(const bclient* clt, const std::shared_ptr<brequest>& req, std::shared_ptr<bresponse>& rep)
{
	if (curl_ == nullptr || clt == nullptr)
		return false;
	/*set no signal*/
	if (CURLE_OK != curl_easy_setopt(curl_, CURLOPT_NOSIGNAL, 1L))
		return false;
	/*set accept encoding*/
	if (CURLE_OK != curl_easy_setopt(curl_, CURLOPT_ACCEPT_ENCODING, ""))
		return false;
	/*set timeout*/
	if (CURLE_OK != curl_easy_setopt(curl_, CURLOPT_TIMEOUT, clt->timeout_read()))
		return false;
	/*set connect timeout*/
	if (CURLE_OK != curl_easy_setopt(curl_, CURLOPT_CONNECTTIMEOUT, clt->timeout_connect()))
		return false;
	/*set ssl*/
	auto ssl_ca = clt->ssl_verfication();
	if (ssl_ca.empty())
	{
		if (CURLE_OK != curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYPEER, false))
			return false;
		if (CURLE_OK != curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYHOST, false))
			return false;
	}
	else
	{
		if (CURLE_OK != curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYPEER, 1L))
			return false;
		if (CURLE_OK != curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYHOST, 2L))
			return false;
		if (CURLE_OK != curl_easy_setopt(curl_, CURLOPT_CAINFO, ssl_ca.c_str()))
			return false;
	}
	/*set header*/
	auto hds = req->headers();
	if (!hds.empty())
	{
		for (const auto& hd: hds)
		{
			headers_ = curl_slist_append(headers_, hd.c_str());
		}
		if (CURLE_OK != curl_easy_setopt(curl_, CURLOPT_HTTPHEADER, headers_))
			return false;
	}
	/*set main option*/
	return (CURLE_OK == curl_easy_setopt(curl_, CURLOPT_URL, req->url()))
		&& (CURLE_OK == curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, write_callback))
		&& (CURLE_OK == curl_easy_setopt(curl_, CURLOPT_WRITEDATA, &(rep->body())))
		&& (CURLE_OK == curl_easy_setopt(curl_, CURLOPT_HEADERFUNCTION, write_callback))
		&& (CURLE_OK == curl_easy_setopt(curl_, CURLOPT_HEADERDATA, &(rep->headers())))
		&& (CURLE_OK == curl_easy_setopt(curl_, CURLOPT_ERRORBUFFER, rep->error()));
}

bool bhttp::bcurl::mime_init(const std::shared_ptr<brequest>& req)
{
	mime_ = curl_mime_init(curl_);

	auto forms = req->forms();
	for (auto item : forms)
	{
		auto part = curl_mime_addpart(mime_);
		if (!item.name.empty()) curl_mime_name(part, item.name.c_str());
		if (!item.data.empty()) curl_mime_data(part, item.data.c_str(), item.data.size());
		if (!item.ctype.empty()) curl_mime_type(part, item.ctype.c_str());
		if (!item.fname.empty()) curl_mime_filename(part, item.fname.c_str());
		if (!item.fpath.empty()) curl_mime_filedata(part, item.fpath.c_str());
	}

	return CURLE_OK == curl_easy_setopt(curl_, CURLOPT_MIMEPOST, mime_);
}

bool bcurl::perform(int* rep_code)
{
    CURLcode code = curl_easy_perform(curl_);
    if (code != CURLE_OK)
    {
        fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(code));
        return false;
    }
    int port = 0;
    curl_easy_getinfo(curl_, CURLINFO_LOCAL_PORT, &port);
    fprintf(stdout, "CURLINFO_LOCAL_PORT: %d\n", port);
	code = curl_easy_getinfo(curl_, CURLINFO_RESPONSE_CODE, rep_code);
	if (code != CURLE_OK)
	{
		fprintf(stderr, "curl_easy_getinfo() failed: %s\n", curl_easy_strerror(code));
		return false;
	}
	return true;
}
